package common

import (
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"net"
	nurl "net/url"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"

	//log "ycx-glog"
)

var addrCache = make(map[string]*net.TCPAddr, 0)

var SHARD_COUNT = uint32(32)

func fnv32(key []byte) uint32 {
	hash := uint32(2166136261)
	const prime32 = uint32(16777619)
	for i := 0; i < len(key); i++ {
		hash *= prime32
		hash ^= uint32(key[i])
	}
	return hash
}

func IPart(key []byte) uint32 {
	return uint32(fnv32(key)) % SHARD_COUNT
}

func IParts() []uint32 {
	var i uint32
	ks := make([]uint32, 0)
	for i = 0; i < SHARD_COUNT; i++ {
		ks = append(ks, i)
	}
	return ks
}

func StructToString(obj interface{}) string {
	res, _ := json.MarshalIndent(obj, "", "  ")
	s := string(res)
	return s
}

func PrintStruct(obj interface{}) {
	fmt.Println(StructToString(obj))
}

func Pauseln(a ...interface{}) {
	fmt.Println(a...)
	time.Sleep(1 * 1e15)
}

func Pause(i int) {
	if i <= 0 {
		fmt.Println("pause...")
		time.Sleep(1 * 1e15)
	} else {
		time.Sleep(time.Duration(i) * 1e6)
	}
}

func JsonEncode(data interface{}) []byte {
	if data == nil {
		return nil
	}
	out, err := json.Marshal(data)
	if err != nil {
		return nil
	}
	return out
}

func JsonDecode(data []byte) (interface{}, error) {
	l := len(data)
	if l == 0 {
		return nil, nil
	}
	c := data[0]
	if c == '"' {
		var result string
		err := json.Unmarshal(data, &result)
		if err != nil {
			return nil, err
		}
		return result, nil
	} else if c == '{' || c == '[' {
		var result interface{}
		result = make(map[string]interface{})
		err := json.Unmarshal(data, &result)
		if err != nil {
			return nil, err
		}
		return result, nil
	} else if l <= 5 && (string(data) == "true" || string(data) == "false") {
		var result bool
		err := json.Unmarshal(data, &result)
		if err != nil {
			return nil, err
		}
		return result, nil
	} else {
		var result float64
		err := json.Unmarshal(data, &result)
		if err != nil {
			return nil, err
		}
		return result, nil
	}
}

func JsonDecodeMap(data []byte) (mp map[string]interface{}) {
	r, err := JsonDecode(data)
	if err != nil {
		return
	}
	m, ok := r.(map[string]interface{})
	if ok {
		return m
	}
	return
}

func Dump(fileName string, n interface{}) error {
	out_f, err := os.OpenFile(fileName, os.O_CREATE|os.O_RDWR, 0777)
	if err != nil {
		return err
	}
	defer out_f.Close()
	encoder := json.NewEncoder(out_f)
	encoder.SetIndent("", "  ")
	err = encoder.Encode(n)
	if err != nil {
		return err
	}
	return nil
}

func Load(fileName string, n interface{}) error {
	in_f, err := os.Open(fileName)
	if err != nil {
		return err
	}
	defer in_f.Close()
	decoder := json.NewDecoder(in_f)
	err = decoder.Decode(n)
	if err != nil {
		return err
	}
	return nil
}

func ResolveTCPAddr(addr string) *net.TCPAddr {
	if len(addr) == 0 {
		return nil
	}
	addrTcp, ok := addrCache[addr]
	if ok {
		return addrTcp
	}
	addrTcp, _ = net.ResolveTCPAddr("tcp", addr)
	return addrTcp
}

func TypeOf(in interface{}) string {
	return reflect.TypeOf(in).Name()
}

func JEncode(data interface{}) []byte {
	if data == nil {
		return nil
	}

	switch data.(type) {
	case string:
		return []byte(data.(string))
	case []byte:
		return []byte(data.([]byte))
	}
	out, err := json.Marshal(data)
	if err != nil {
		return nil
	}
	return out
}

func JDecode(data []byte, result interface{}) error {
	switch result.(type) {
	case *string:
		reflect.ValueOf(result).Elem().Set(reflect.ValueOf(string(data)))
		return nil
	case *[]byte:
		reflect.ValueOf(result).Elem().Set(reflect.ValueOf(data))
		return nil
	}

	err := json.Unmarshal(data, result)
	if err != nil {
		return err
	}
	return nil
}

func ChanTryIn(c chan interface{}, v interface{}) {
	defer func() {
		recover()
	}()
	c <- v
}

func ChanClose(c chan interface{}) {
	defer func() {
		recover()
	}()
	close(c)
	for {
		if len(c) > 0 {
			_, ok := <-c
			if !ok {
				break
			}
		} else {
			break
		}

	}
}

func GetTCPAddr(baddr string) (*net.TCPAddr, error) {
	addr, err := net.ResolveTCPAddr("tcp", baddr)
	if err != nil {
		return nil, err
	}
	for i := 10; i > 0; i-- {
		l, err := net.ListenTCP("tcp", addr)
		if err != nil {
			if l != nil {
				l.Close()
			}
			addr.Port++
			continue
		}
		addr = l.Addr().(*net.TCPAddr)
		l.Close()
		return addr, nil
	}
	return nil, errors.New("get tcp port fail")
}

func FixUrl(url string, baseUrl string) string {
	url = strings.TrimSpace(url)
	url = strings.Replace(url, " ", "", -1)
	url = strings.Replace(url, "\r", "", -1)
	url = strings.Replace(url, "\n", "", -1)
	lash := strings.Index(url, "#")
	if lash > 0 {
		url = url[:lash]
	}

	lash = strings.Index(url, "://")
	if lash > 0 {
		return url
	}

	blash := strings.Index(baseUrl, "://")
	if blash < 1 {
		return ""
	}

	bu, err := nurl.Parse(baseUrl)
	if err != nil {
		return ""
	}

	if lash == 0 {
		return bu.Scheme + url
	}

	lash = strings.Index(url, "//")
	if lash == 0 {
		return bu.Scheme + ":" + url
	}

	url = strings.Replace(url, `\`, `/`, -1)
	url = strings.Replace(url, `//`, `/`, -1)

	if len(url) > 1 && url[0] == '.' && url[1] == '/' {
		url = url[1:]
	}

	ul := len(url)
	if ul == 0 {
		return bu.Scheme + `://` + bu.Host
	}
	if url[0] == '/' {
		return bu.Scheme + `://` + bu.Host + url
	}

	lash = strings.Index(url, `?`)
	path := url
	query := ""
	if lash > 0 {
		path = url[:lash]
		query = url[lash:]
	}

	blash = strings.LastIndex(bu.Path, `/`)
	bpath := `/`
	if blash > 0 {
		bpath = bu.Path[:blash+1]
	}

	path = bpath + path
	for {
		lash = strings.Index(path, `/../`)
		if lash < 0 {
			break
		}
		hpath := path[:lash]
		hlash := strings.LastIndex(hpath, `/`)
		if hlash >= 0 {
			hpath = hpath[:hlash]
		}

		path = hpath + path[lash+3:]
	}

	return bu.Scheme + `://` + bu.Host + path + query
}

func ParseTimes(str string) [][]time.Time {
	strs := strings.Split(str, ";")
	tms := make([][]time.Time, 0)
	var err error
	for _, s := range strs {
		strIns := strings.Split(s, "~")
		if len(strIns) >= 2 {
			tmIns := make([]time.Time, 2)
			ll := len(strIns[0])
			if ll > 0 {
				if ll == 4 {
					strIns[0] = "0" + strIns[0]
				} else if ll == 3 {
					strIns[0] = "00" + strIns[0]
				} else if ll == 2 {
					strIns[0] = "00:" + strIns[0]
				} else if ll == 1 {
					strIns[0] = "00:0" + strIns[0]
				}
				tmIns[0], err = TimeParseHHmm(strings.TrimSpace(strIns[0]))
				if err != nil {
					continue
				}
			} else {
				continue
			}
			ll = len(strIns[1])
			if ll > 0 {
				if ll == 4 {
					strIns[1] = "0" + strIns[1]
				} else if ll == 3 {
					strIns[1] = "00" + strIns[1]
				} else if ll == 2 {
					strIns[1] = "00:" + strIns[1]
				} else if ll == 1 {
					strIns[1] = "00:0" + strIns[1]
				}
				tmIns[1], err = TimeParseHHmm(strings.TrimSpace(strIns[1]))
				if err != nil {
					continue
				}
			} else {
				continue
			}
			tms = append(tms, tmIns)
		}
	}
	return tms
}

func TimeRun(timer *ITimer, delayRun bool, fn func()) {
	if !delayRun {
		go fn()
	}
	for {
		select {
		case <-timer.C:
			if timer.IsExit() {
				return
			}
			timer.reset()
			go fn()
		}
	}
}

func PartTimeUnix(times [][]time.Time) [][]int64 {
	res := make([][]int64, 0)
	zero, _ := TimeParseHHmm("00:00")
	for _, tms := range times {
		res = append(res, []int64{
			zero.Unix(),
			tms[0].Unix(),
			tms[1].Unix(),
		})
	}
	return res
}

func GetTimeTag(times [][]time.Time) int {
	nowT := time.Now().Unix()
	var timeLen int64
	for _, tms := range times {
		tm1 := tms[0].Unix()
		tm2 := tms[1].Unix()
		if nowT >= tm1 && nowT < tm2 {
			timeLen += nowT - tm1
			return int(float64(timeLen) / 60.0)
		}
		timeLen += tm2 - tm1
	}
	return -1
}

func GetTimeTagMax(times [][]time.Time) int {
	var timeLen int64
	for _, tms := range times {
		tm1 := tms[0].Unix()
		tm2 := tms[1].Unix()
		timeLen += tm2 - tm1
	}
	return int(float64(timeLen) / 60.0)
}

func GetTimeTagCompare(times [][]time.Time) int {
	nowT := time.Now().Unix()
	min := int64(math.MaxInt64)
	max := int64(0)
	for _, tms := range times {
		tm1 := tms[0].Unix()
		tm2 := tms[1].Unix()
		if min > tm1 {
			min = tm1
		}
		if max < tm2 {
			max = tm2
		}
	}
	if nowT > max {
		return 1
	} else if nowT < min {
		return -1
	}
	return 0
}

func SendChanMessage(c chan string, msg string) {
	defer func() {
		recover()
	}()
	c <- msg
}

func FenToYuan(i uint64) float64 {
	return float64(i) / 100.0
}

func ArrayInterfaceToInt(arr []interface{}) []int {
	sz := len(arr)
	res := make([]int, sz)
	for i := 0; i < sz; i++ {
		res[i] = ValueToInt(arr[i], 0)
	}
	return res
}

func DayTagToWeekTag(d uint32) uint32 {
	r, _ := TimeParseyyyyMMdd(strconv.Itoa(int(d)))
	year, week := r.ISOWeek()
	return uint32(year*1000 + week*10 + int(r.Weekday()))
}

func MaxMinPrice(preClosePrice uint64, limitValue float64) (uint64, uint64) {
	if limitValue <= 0.0 {
		return 0, math.MaxUint64
	}
	if limitValue > 1.0 {
		limitValue = 1.0
	}
	return uint64((1.0 + limitValue) * float64(preClosePrice)), uint64((1.0 - limitValue) * float64(preClosePrice))
}
